package com.itdct.onflow.core.util

import cn.hutool.core.util.ReflectUtil
import com.itdct.onflow.core.extend.logDebug
import java.lang.reflect.Method
import java.math.BigDecimal
import java.util.*

/**
 * @author Zhouwx
 * @date 2024/4/18 11:11:32
 * @version 1.0
 * @description
 */
object KBeanCopyUtil {
    inline fun <reified T> injectValue(source: Any, target: T, ignoreNullValue: Boolean = true, vararg ignoreFields: String) {
        val sourceMethods = ReflectUtil.getMethods(source::class.java)
        handleMethods<T>(sourceMethods, source, ignoreNullValue, ignoreFields, T::class.java, target)
    }

    fun <T> copyNewInstance(source: Any, targetClazz: Class<T>, ignoreNullValue: Boolean = true, vararg ignoreFields: String): T {
        val target = targetClazz.getDeclaredConstructor().newInstance() as T

        val sourceClass = source::class.java
        val sourceMethods = ReflectUtil.getMethods(sourceClass)
        handleMethods<T>(sourceMethods, source, ignoreNullValue, ignoreFields, targetClazz, target)
        return target
    }

    fun <T> copyNewListInstance(sourceList: List<Any>, targetClazz: Class<T>, ignoreNullValue: Boolean = true, vararg ignoreFields: String): List<T> {
        val list = mutableListOf<T>()
        for (source in sourceList) {
            val t = copyNewInstance<T>(source, targetClazz, ignoreNullValue, *ignoreFields)
            list.add(t)
        }
        return list
    }

    fun <T> handleMethods(
        sourceMethods: Array<Method>,
        source: Any,
        ignoreNullValue: Boolean,
        ignoreFields: Array<out String>,
        targetClazz: Class<T>,
        target: T
    ) {
        for (sourceMethod in sourceMethods) {
            val functionName = sourceMethod.name

            if (!functionName.startsWith("get") && !functionName.startsWith("is")) {
                continue
            }
            var prefixLength = 0
            if (functionName.startsWith("get")) {
                prefixLength = 3
            } else if (functionName.startsWith("is")) {
                prefixLength = 2
            }

            val invokeValue = sourceMethod.invoke(source)
            if (invokeValue == null && ignoreNullValue) {
                continue
            }

            var fieldName = functionName.substring(prefixLength)
            fieldName = fieldName.substring(0, 1).lowercase() + fieldName.substring(1)

            val nowLevelIgnoreFields = ignoreFields.filter { !it.contains(".") }
            if (nowLevelIgnoreFields.contains(fieldName)) {
                continue
            }

            val targetGetMethod: Method
            try {
                targetGetMethod = targetClazz.getMethod("get${functionName.substring(prefixLength)}")
            } catch (e: Exception) {
                continue
            }

            val targetReturnType = targetGetMethod.returnType
            val targetSetMethod: Method
            try {
                targetSetMethod = targetClazz.getMethod("set${functionName.substring(prefixLength)}", targetReturnType)
            } catch (e: Exception) {
                continue
            }

            when (invokeValue) {
                is Byte,
                is Float,
                is Double,
                is Short,
                is BigDecimal,
                is String,
                is Char,
                is Boolean,
                is Long,
                is Date,
                is Int -> {
                    // INFO: Zhouwx: 2024/4/18 基础类型，直接赋值
                    try {
                        targetSetMethod.invoke(target, invokeValue)
                    } catch (e: Exception) {
                        logDebug("赋值失败", e)
                    }
                }

                else -> {
                    // INFO: Zhouwx: 2024/4/18 非基础类型 需要递归赋值
                    val parameter = targetSetMethod.parameters[0]
                    val belongThisIgnoreFields = ignoreFields.filter { it.startsWith("${fieldName}.") }.map { it.substring("${fieldName}.".length) }
                    if (invokeValue != null) {
                        val innerEntity = copyNewInstance(invokeValue, parameter.type, ignoreNullValue, *belongThisIgnoreFields.toTypedArray())
                        try {
                            targetSetMethod.invoke(target, innerEntity)
                        } catch (e: Exception) {
                            logDebug("赋值失败", e)
                        }
                    } else {
                        try {
                            targetSetMethod.invoke(target, null)
                        } catch (e: Exception) {
                            logDebug("赋值null失败")
                        }
                    }
                }
            }
        }
    }
}

